package com.edgarli.common.utils;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.function.BinaryOperator;

/**
 * @author EdgarLi
 * @Description Bigdecimal 公共方法
 * @create 2024-09-07 20:36
 */
public class ArithUtil {

    /**
     * 加法操作式
     */
    public static BinaryOperator<BigDecimal> addOperator = (a, b) -> ArithUtil.add(a, b);
    /**
     * 减法操作式
     */
    public static BinaryOperator<BigDecimal> subOperator = (a, b) -> ArithUtil.sub(a, b);
    /**
     * 乘法操作式
     */
    public static BinaryOperator<BigDecimal> mulOperator = (a, b) -> ArithUtil.mul(a, b);


    /**
     * 提供精确的加法运算。
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(Number v1, Number v2) {
        v1 = null == v1 ? BigDecimal.ZERO : v1;
        v2 = null == v2 ? BigDecimal.ZERO : v2;
        BigDecimal b1 = new BigDecimal(v1.toString());
        BigDecimal b2 = new BigDecimal(v2.toString());
        return b1.add(b2);
    }


    /**
     * 提供精确的加法运算。
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(Number v1, Number v2, int newScale, int roundingMode) {
        v1 = null == v1 ? BigDecimal.ZERO : v1;
        v2 = null == v2 ? BigDecimal.ZERO : v2;
        BigDecimal b1 = new BigDecimal(v1.toString());
        BigDecimal b2 = new BigDecimal(v2.toString());
        return b1.add(b2).setScale(newScale, roundingMode);
    }

    /**
     * 提供精确的减法运算。
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(Number v1, Number v2) {
        v1 = null == v1 ? BigDecimal.ZERO : v1;
        v2 = null == v2 ? BigDecimal.ZERO : v2;
        BigDecimal b1 = new BigDecimal(v1.toString());
        BigDecimal b2 = new BigDecimal(v2.toString());
        return b1.subtract(b2);
    }

    /**
     * 提供精确的减法运算。
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(Number v1, Number v2, int newScale, int roundingMode) {
        v1 = null == v1 ? BigDecimal.ZERO : v1;
        v2 = null == v2 ? BigDecimal.ZERO : v2;
        BigDecimal b1 = new BigDecimal(v1.toString());
        BigDecimal b2 = new BigDecimal(v2.toString());
        return b1.subtract(b2).setScale(newScale, roundingMode);
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(Number v1, Number v2) {
        v1 = null == v1 ? BigDecimal.ZERO : v1;
        v2 = null == v2 ? BigDecimal.ZERO : v2;
        BigDecimal b1 = new BigDecimal(v1.toString());
        BigDecimal b2 = new BigDecimal(v2.toString());
        return b1.multiply(b2);
    }

    public static BigDecimal mul(Number v1, Number v2, int scale) {
        return mul(v1, v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到 小数点以后 Constant.DEF_DIV_SCALE 位，以后的数字四舍五入。
     *
     * @param divisor  除数
     * @param dividend 被除数
     * @return 两个参数的商
     */
    public static BigDecimal div(Number divisor, Number dividend) {
        return div(divisor, dividend, 6);
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指 定精度，以后的数字四舍五入。
     *
     * @param divisor  除数
     * @param dividend 被除数
     * @param scale    表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static BigDecimal div(Number divisor, Number dividend, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        divisor = null == divisor ? BigDecimal.ZERO : divisor;
        if (dividend == null) {
            return BigDecimal.ZERO;
        }
        BigDecimal b1 = new BigDecimal(divisor.toString());
        BigDecimal b2 = new BigDecimal(dividend.toString());

        if (equalZero(b2)) {
            return BigDecimal.ZERO;
        }
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指 定精度(无四舍五入)
     *
     * @param divisor  除数
     * @param dividend 被除数
     * @param scale    表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static BigDecimal divNoRounding(Number divisor, Number dividend, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(divisor.toString());
        BigDecimal b2 = new BigDecimal(dividend.toString());

        return b1.divide(b2, scale, BigDecimal.ROUND_DOWN);
    }

    /**
     * @param v
     * @return
     */
    public static boolean gtZero(BigDecimal v) {
        if (v == null) {
            return false;
        }
        return BigDecimal.ZERO.compareTo(v) < 0;
    }

    /**
     * 是否为0
     *
     * @param v
     * @return
     */
    public static boolean equalZero(BigDecimal v) {
        return BigDecimal.ZERO.compareTo(v) == 0;
    }

    public static boolean equal(Number v1, Number v2) {
        if (v1 == null || v2 == null) {
            return Objects.equals(v1, v2);
        }
        return new BigDecimal(v1.toString()).compareTo(new BigDecimal(v2.toString())) == 0;
    }

    /**
     * 计算占比 返回的是 乘 100 的
     *
     * @param v1
     * @param v2
     * @param scale
     * @return
     */
    public static BigDecimal calcZb(Number v1, Number v2, int scale) {
        if (v1 == null) {
            v1 = BigDecimal.ZERO;
        }
        return ArithUtil.div(ArithUtil.mul(v1, 100), v2, scale);
    }

    public static boolean inRange(Number value, Number min, Number max) {
        BigDecimal valueBg = new BigDecimal(value.toString()), minBg = new BigDecimal(min.toString()), maxBg = new BigDecimal(max.toString());

        return minBg.compareTo(valueBg) <= 0 && valueBg.compareTo(maxBg) <= 0;
    }
}
